]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
workspace: Track windows in WindowClone layout manager
authorJonas Dreßler <verdre@v0yd.nl>
Mon, 1 Jun 2020 12:41:25 +0000 (14:41 +0200)
committerGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>
Thu, 4 Jun 2020 17:40:08 +0000 (17:40 +0000)
Move the tracking of the bounding box and all the layout related things
out of the WindowClone class and into the layout manager. This allows
the layout manager to keep track of its windows itself and simply notify
the new bounding-box property in case that box changes.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1297

js/ui/workspace.js

index 00a15b36a843c6def523fc713e4b3f74f267d79e..509172f7e61ac369364346b5bde3c9a8aa711416 100644 (file)
@@ -34,61 +34,134 @@ function _interpolate(start, end, step) {
     return start + (end - start) * step;
 }
 
-var WindowCloneLayout = GObject.registerClass(
-class WindowCloneLayout extends Clutter.LayoutManager {
-    _init(boundingBox) {
+var WindowCloneLayout = GObject.registerClass({
+    Properties: {
+        'bounding-box': GObject.ParamSpec.boxed(
+            'bounding-box', 'Bounding box', 'Bounding box',
+            GObject.ParamFlags.READABLE,
+            Clutter.ActorBox.$gtype),
+    },
+}, class WindowCloneLayout extends Clutter.LayoutManager {
+    _init() {
         super._init();
 
-        this._boundingBox = boundingBox;
+        this._container = null;
+        this._boundingBox = new Clutter.ActorBox();
+        this._windows = new Map();
     }
 
-    get boundingBox() {
-        return this._boundingBox;
-    }
+    _layoutChanged() {
+        let frameRect;
+
+        for (const windowInfo of this._windows.values()) {
+            const frame = windowInfo.metaWindow.get_frame_rect();
+            frameRect = frameRect ? frameRect.union(frame) : frame;
+        }
+
+        if (!frameRect)
+            frameRect = new Meta.Rectangle();
+
+        const oldBox = this._boundingBox.copy();
+        this._boundingBox.set_origin(frameRect.x, frameRect.y);
+        this._boundingBox.set_size(frameRect.width, frameRect.height);
 
-    set boundingBox(b) {
-        this._boundingBox = b;
+        if (!this._boundingBox.equal(oldBox))
+            this.notify('bounding-box');
+
+        // Always call layout_changed(), a size or position change of an
+        // attached dialog might not affect the boundingBox
         this.layout_changed();
     }
 
+    vfunc_set_container(container) {
+        this._container = container;
+    }
+
     vfunc_get_preferred_height(_container, _forWidth) {
-        return [0, this._boundingBox.height];
+        return [0, this._boundingBox.get_height()];
     }
 
     vfunc_get_preferred_width(_container, _forHeight) {
-        return [0, this._boundingBox.width];
+        return [0, this._boundingBox.get_width()];
     }
 
     vfunc_allocate(container, box) {
         // If the scale isn't 1, we weren't allocated our preferred size
         // and have to scale the children allocations accordingly.
-        const scaleX = box.get_width() / this._boundingBox.width;
-        const scaleY = box.get_height() / this._boundingBox.height;
+        const scaleX = box.get_width() / this._boundingBox.get_width();
+        const scaleY = box.get_height() / this._boundingBox.get_height();
 
         const childBox = new Clutter.ActorBox();
 
-        container.get_children().forEach(child => {
-            let realWindow;
-            if (child == container._windowClone)
-                realWindow = container.realWindow;
-            else
-                realWindow = child.source;
+        for (const child of container) {
+            if (!child.visible)
+                continue;
 
-            const bufferRect = realWindow.meta_window.get_buffer_rect();
-            childBox.set_origin(
-                bufferRect.x - this._boundingBox.x,
-                bufferRect.y - this._boundingBox.y);
+            const windowInfo = this._windows.get(child);
+            if (windowInfo) {
+                const bufferRect = windowInfo.metaWindow.get_buffer_rect();
+                childBox.set_origin(
+                    bufferRect.x - this._boundingBox.x1,
+                    bufferRect.y - this._boundingBox.y1);
 
-            const [, , natWidth, natHeight] = child.get_preferred_size();
-            childBox.set_size(natWidth, natHeight);
+                const [, , natWidth, natHeight] = child.get_preferred_size();
+                childBox.set_size(natWidth, natHeight);
 
-            childBox.x1 *= scaleX;
-            childBox.x2 *= scaleX;
-            childBox.y1 *= scaleY;
-            childBox.y2 *= scaleY;
+                childBox.x1 *= scaleX;
+                childBox.x2 *= scaleX;
+                childBox.y1 *= scaleY;
+                childBox.y2 *= scaleY;
+
+                child.allocate(childBox);
+            } else {
+                child.allocate_preferred_size();
+            }
+        }
+    }
 
-            child.allocate(childBox);
+    addWindow(clone, metaWindow) {
+        if (this._windows.has(clone))
+            return;
+
+        const windowActor = metaWindow.get_compositor_private();
+
+        this._windows.set(clone, {
+            metaWindow,
+            windowActor,
+            sizeChangedId: metaWindow.connect('size-changed', () =>
+                this._layoutChanged()),
+            positionChangedId: metaWindow.connect('position-changed', () =>
+                this._layoutChanged()),
+            destroyId: windowActor.connect('destroy', () =>
+                clone.destroy()),
+            cloneDestroyId: clone.connect('destroy', () =>
+                this.removeWindow(clone)),
         });
+
+        this._container.add_child(clone);
+
+        this._layoutChanged();
+    }
+
+    removeWindow(clone) {
+        const windowInfo = this._windows.get(clone);
+        if (!windowInfo)
+            return;
+
+        windowInfo.metaWindow.disconnect(windowInfo.sizeChangedId);
+        windowInfo.metaWindow.disconnect(windowInfo.positionChangedId);
+        windowInfo.windowActor.disconnect(windowInfo.destroyId);
+        clone.disconnect(windowInfo.cloneDestroyId);
+
+        this._windows.delete(clone);
+        this._container.remove_child(clone);
+
+        this._layoutChanged();
+    }
+
+    // eslint-disable-next-line camelcase
+    get bounding_box() {
+        return this._boundingBox;
     }
 });
 
@@ -130,7 +203,7 @@ var WindowClone = GObject.registerClass({
 
         this.set_offscreen_redirect(Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY);
 
-        this.add_child(this._windowClone);
+        this.layout_manager.addWindow(this._windowClone, this.metaWindow);
 
         this._delegate = this;
 
@@ -144,13 +217,7 @@ var WindowClone = GObject.registerClass({
         this._windowClone._posChangedId = this.metaWindow.connect('position-changed',
             this._computeBoundingBox.bind(this));
         this._windowClone._destroyId =
-            this.realWindow.connect('destroy', () => {
-                // First destroy the clone and then destroy everything
-                // This will ensure that we never see it in the
-                // _disconnectSignals loop
-                this._windowClone.destroy();
-                this.destroy();
-            });
+            this.realWindow.connect('destroy', () => this.destroy());
 
         this._updateAttachedDialogs();
         this._computeBoundingBox();
@@ -235,15 +302,12 @@ var WindowClone = GObject.registerClass({
             this._onMetaWindowSizeChanged.bind(this));
         clone._posChangedId = metaWin.connect('position-changed',
             this._onMetaWindowSizeChanged.bind(this));
-        clone._destroyId = realWin.connect('destroy', () => {
-            clone.destroy();
-
-            this._onMetaWindowSizeChanged();
-        });
+        clone._destroyId = realWin.connect('destroy',
+            this._onMetaWindowSizeChanged.bind(this));
 
         Shell.util_set_hidden_from_pick(clone, true);
 
-        this.add_child(clone);
+        this.layout_manager.addWindow(clone, metaWin);
     }
 
     _updateAttachedDialogs() {
@@ -282,7 +346,6 @@ var WindowClone = GObject.registerClass({
 
         // Convert from a MetaRectangle to a native JS object
         this._boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
-        this.layout_manager.boundingBox = rect;
     }
 
     get windowCenter() {